/**
 * 类型保护相关泛型工具
 */

/* 
  1 学到的几个有意思的东西
  1 怎么判断一个属性是可选的还是必选类型？
  答：{} extends {name?: string} 这会成立，说明空对象是可选类型参数对象子类型
  {} extends { name:string } 这不成立
  这样就可以判断可选和必选类型
  2 还可以给泛型赋值默认值
  type Optional<T extends object, K extends keyof T = keyof T> = {}
  3 如何判断两个值绝对相等？
  还是通过extends双重判断
  val1 val2 两个类型
  [val] extends [val2] ? [val2] extends [val] ? true : false : false

*/
export const a = 0;
export type Primitive = string | boolean | number | symbol | bigint | null | undefined;

export const isPrimitive = (value: any): value is Primitive => {
  if (value === null || value === undefined) {
    return true;
  }
  switch (typeof value) {
    case 'string':
    case 'boolean':
    case 'number':
    case 'symbol':
    case 'bigint':
      return true;
    default:
      return false;
  }
};

export type Falsy = false | '' | 0 | undefined | null;

export const isFalsy = (value: any): value is Falsy => {
  return !value;
};

export type Nullish = null | undefined;
export const isNullish = (val: any): val is Nullish => {
  return val == null;
};

// 取交集
export type SetIntersection<T, U> = T extends U ? T : never;

// 取差集
export type SetDiff<T, U> = T extends U ? never : T;

// 剔除T中U的部分
export type Exclude<T, U> = T extends U ? never : T;

export type Excrate<T, U> = T extends U ? T : never;

// 剔除null和undefined
export type NonNullable<T> = T extends Nullish ? never : T;

// 剔除undefined
export type NonUndefined<T> = T extends undefined ? never : T;

// 获取类型的方法名（返回联合类型）
export type FunctionKeys<T extends object> = {
  [K in keyof T]-?: T[K] extends Function ? K : never;
}[keyof T];

// 获取类型的非方法名（返回联合类型）
/* 
  注意：这边必须使用-?将可选属性变成必选类型，否则会多出一个undefined的key
 */
export type NonFunctionKeys<T extends object> = {
  [K in keyof T]-?: T[K] extends Function ? never : K;
}[keyof T];

export type Test<T extends object> = {
  [K in keyof T]-?: T[K] extends Function ? never : K;
};

// 返回除了readonly外的key值
export type MutableKeys<T extends object> = {
  [K in keyof T]: T[K];
};

// 从类型中取K的值
export type Pick<T extends object, K extends keyof T> = {
  [Key in K]: T[Key];
};

export type PickByValue<T extends object, V> = Pick<
  T,
  {
    [key in keyof T]: T[key] extends V ? key : never;
  }[keyof T]
>;

/**
 * 原理：Pick<T, key>如果对应的是一盒可选类型，那么返回{key?: number}那么 {} extends {key?: number}刚好成立，
 * 说明这个属性是可选类型，而如果是必选类型，那么{} extends {key: number}条件不成立
 */
export type RequiredKeys<T extends object> = {
  [K in keyof T]-?: {} extends Pick<T, K> ? never : K;
}[keyof T];

export type OptionKeys<T extends object> = {
  [K in keyof T]-?: {} extends Pick<T, K> ? K : never;
}[keyof T];

export type Omit<T extends object, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;

export type OmitByValue<T extends object, V> = Pick<
  T,
  { [key in keyof T]-?: T[key] extends V ? never : key }[keyof T]
>;

// 两个对象类型取差集
export type Diff<T extends object, U extends object> = Pick<T, SetDiff<keyof T, keyof U>>;
// Intersection取交集
export type Intersection<T extends object, U extends object> = Pick<
  T,
  SetIntersection<keyof T, keyof U>
>;

// OmitByValue

// 把对象的某些属性变成可选类型
// 优化点，可以给泛型添加默认值！！！！！！！！！
// K extends keyof T = keyof T
export type Optional<T extends object, K extends keyof T = keyof T> = Omit<T, K> &
  Partial<Pick<T, K>>;

export type Required<T extends object> = {
  readonly [K in keyof T]-?: T[K];
};

export type Readonly<T extends object> = {
  readonly [K in keyof T]: T[K];
};

export type DeepReadonly<T extends object> = {
  [K in keyof T]: T[K] extends object ? never : T[K];
};

export type Mutable<T extends object> = {
  -readonly [K in keyof T]: T[K];
};

export type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer P
  ? P
  : T;
export type PromiseType1<T extends Promise<any>> = T extends Promise<infer P> ? P : never;

type IfEquals<X, Y, A = X, B = never> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y
  ? 1
  : 2
  ? A
  : B;
namespace test {
  interface Person {
    name: string;
    age: number;
    is: boolean;
    setName(): string;
    setAge(): number;
  }

  type funcKeys = FunctionKeys<Person>;
  type nonFuncKeys = NonFunctionKeys<Person>;
  type op = Optional<Person>;
  type pop = op;
  type p = Pick<Person, 'name'>;
  type pp = Pick<Person, 'age'>;
  type e = {} extends Pick<Person, 'age'> ? 'a' : 'b';

  type pickbyvalue = PickByValue<Person, number>;
  type omitbyvalue = OmitByValue<Person, number>;

  // OmitByValue
}
